package com.gitee.sop.layui.adminserver.module.base.util;

import com.gitee.sop.layui.adminserver.module.base.bo.SqlTree;

import java.util.*;

/**
 * @author: huwei
 * @date: 2020/4/28 16:03
 * @version: 1.0.0
 */
public class SqlTreeUtil {

    /**
     * 为指定sql树类型的元素，构建上级节点id，即pid
     *
     * @param tList sql树列表
     * @param <T>   sql树
     * @return
     */
    public static <T extends SqlTree> List<Map<String, Object>> tree(List<T> tList) {
        Map<String, T> codeToInstance = tList.stream().collect(HashMap::new, (res, ele) -> res.put(sqlNodeKey(ele), ele), (l, r) -> {
        });

        return tList.stream().collect(ArrayList::new, (res, ele) -> {
            Integer pid = null;
            String parentKey = sqlParentNodeKey(ele);
            T t = codeToInstance.get(parentKey);
            if (t != null) {
                try {
                    pid = (Integer) ObjectUtil.getFieldValue(t, "id");
                } catch (Exception e) {
                    throw new RuntimeException(StringUtil.concat(t.getClass().getName(), "不存在id字段，或id字段类型不为int类型"));
                }
            }
            Map<String, Object> map = ObjectUtil.toMap(ele);
            map.put("pid", pid == null ? 0 : pid);
            res.add(map);
        }, (l, r) -> {
        });
    }

    /**
     * 从指定列表中获取指定节点的所有子节点 ,不包含父节点
     *
     * @param tList 树列表
     * @param t     树节点
     * @param <T>
     * @return
     */
    public static <T extends SqlTree> List<T> getChildList(List<T> tList, T t) {
        Map<String, List<T>> parentKeyToMap = tList.stream().collect(HashMap::new, (res, ele) -> {
            String parentKey = StringUtil.concat(ele.getTreeGroup(), ele.getTreeParentLeftTraverseNumber());
            List<T> childList = res.get(parentKey);
            if (childList == null) {
                childList = new ArrayList<>();
                res.put(parentKey, childList);
            }
            childList.add(ele);
        }, (l, r) -> {
        });

        List<T> result = new ArrayList<>();
        //父级队列，用于遍历获取每一个节点的子类
        Queue<T> parentQueue = new LinkedList<>();
        parentQueue.offer(t);
        do {
            T parent = parentQueue.poll();
            String key = StringUtil.concat(parent.getTreeGroup(), parent.getTreeLeftTraverseNumber());
            List<T> childList = parentKeyToMap.get(key);
            if (childList != null) {
                //子节点，加入结果集
                result.addAll(childList);

                //加入队列，用于查询其子节点
                childList.forEach(ele -> {
                    parentQueue.offer(ele);
                });
            }
        } while (!parentQueue.isEmpty());

        return result;
    }

    /**
     * 为指定下级节点设置插入时所需信息
     *
     * @param parent 上级节点
     * @param child  孩子节点
     * @param <T>
     */
    public static <T extends SqlTree> void setInertInfo(T parent, T child) {
        child.setTreeGroup(parent.getTreeGroup());
        child.setTreeLevel(parent.getTreeLevel() + 1);
        child.setTreeLeftTraverseNumber(parent.getTreeLeftTraverseNumber() + 1);
        child.setTreeParentLeftTraverseNumber(parent.getTreeLeftTraverseNumber());
    }

    public static <T extends SqlTree> String sqlNodeKey(T t) {
        return StringUtil.concat(t.getTreeGroup(), t.getTreeLeftTraverseNumber());
    }

    public static <T extends SqlTree> String sqlParentNodeKey(T t) {
        return StringUtil.concat(t.getTreeGroup(), t.getTreeParentLeftTraverseNumber());
    }
}
